/*
 * Copyright 2008 Max Kugland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.splink.library.loading {
	import org.splink.library.queue.Progress;
	import org.splink.library.queue.QEvent;
	import org.splink.library.queue.Qable;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.getTimer;

	/**
	 * The <code>QUrlLoader</code> class loads data from a URL as text, binary data, or URL-encoded 
	 * variables. It wraps the native <code>URLLoader</code> class and as it inherits from <code>Qable</code>
	 * a <code>QUrlLoader</code> can be used with a <code>IQ</code>.
	 * 
	 * @see org.splink.library.queue.Qable;
	 * @see flash.net.URLLoader	 * @see flash.net.URLRequest
	 * 
	 * @author Max Kugland
	 */
	public class QUrlLoader extends Qable {
		private var _urlRequest : URLRequest;
		private var _loader : URLLoader;

		private var _fileSize : uint;
		private var _startTime : int;
		private var _bandwidth : Number = -1;

		/**
		 * @param urlRequest the file to be loaded
		 * @param dataFormat controls whether the downloaded data is received as text, raw binary 
		 * data, or URL-encoded variables. The default value is text. @see URLLoaderDataFormat
		 * @param id the id for the current <code>QUrlLoader</code> instance
		 */
		public function QUrlLoader(urlRequest : URLRequest, dataFormat : String = "text", id : String = "QUrlLoader") {	
			super(id);
			_urlRequest = urlRequest;
			
			_loader = new URLLoader();
			_loader.dataFormat = dataFormat;
		}

		/**
		 * @inheritDoc
		 */
		override protected function doStart() : void {
			_loader.addEventListener(Event.COMPLETE, onLoadComplete);
			_loader.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			_loader.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);			_loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoadError);
			
			_startTime = getTimer();
			try {
				_loader.load(_urlRequest);
			}
            catch (e : SecurityError) {
				error(e.message);
			}
		}

		/**
		 * @inheritDoc
		 */
		override protected function doStop() : void {
			removeListeners();
			
			try {
				_loader.close();
			}
			catch(e : Error) {
			}
		}

		/**
		 * @inheritDoc
		 */
		override protected function doReset() : void {
			doStop();
		}

		private function onLoadProgress(event : ProgressEvent) : void {
			_fileSize = event.bytesTotal;
			
			var progressEvent : QEvent = new QEvent(this, QEvent.PROGRESS, id);
			progressEvent.progress = new Progress(event.bytesLoaded, event.bytesTotal);
			distribute(progressEvent);
		}

		private function onLoadError(event : ErrorEvent) : void {
			removeListeners();
			error(event.text);
		}

		private function onLoadComplete(event : Event) : void {
			removeListeners();
			_bandwidth = Math.floor(_fileSize / (getTimer() - _startTime));
			complete();
		}

		private function removeListeners() : void {
			if(_loader) {
				_loader.removeEventListener(Event.COMPLETE, onLoadComplete);
				_loader.removeEventListener(ProgressEvent.PROGRESS, onLoadProgress);
				_loader.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
				_loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoadError);
			}
		}

		/**
		 * Finalizes the <code>QUrlLoader</code>. <code>QUrlLoader</code> internally uses the flash event model 
		 * without weak references so don't forget to finalize the loader, otherwise it will kept in memory.
		 * 
		 * @inheritDoc
		 */
		override protected function doFinalize() : void {
			removeListeners();
			try {
				_loader.close();
			}
			catch(e : Error) {
			}
			_loader = null;
			_urlRequest = null;
		}

		/**
		 * Provides access to the the loaded data
		 * 
		 * @return the loaded data
		 * @throws Error if content has not been loaded successfully
		 */
		public function getContent() : * {
			try {
				return _loader.data;
			}
			catch(e : Error) {
				throw new Error(this + " Content has not been loaded successfully. " + e.message);
			}
		}

		/**
		 * @return the users bandwidth in kilobytes/second
		 * returns -1 if the bandwidth has not been calculated, yet.
		 */
		public function getBandwidth() : Number {
			return _bandwidth;
		}
	}
}
